#ifndef _MCU_CAN_H_
#define _MCU_CAN_H_

#ifdef __cplusplus
 extern "C" {
#endif

#include "n32g45x.h"
#include "..\ARM\typedef.h"


typedef volatile struct _can_t can_t;

// 帧ID格式
typedef enum _can_ide_t
{
    CAN_IDE_STD = CAN_ID_STD,    // 标准格式
    CAN_IDE_EXT = CAN_ID_EXT     // 扩展格式
}can_ide_t;

// 数据包（消息、帧）类型
typedef enum _can_rtr_t
{
    CAN_RTR_DATA = CAN_RTRQ_DATA,   // 数据帧
    CAN_RTR_REMOTE = CAN_RTRQ_REMOTE  // 遥控帧
}can_rtr_t;

typedef enum _can_baud_t
{
    CAN_BAUD_125K = 125000,
    CAN_BAUD_250K = 250000,
    CAN_BAUD_500K = 500000,
    CAN_BAUD_1M = 1000000
}can_baud_t;

// CAN消息，一包数据，一帧
typedef struct _can_msg_t
{
    u32 ID;         // 帧 ID（标识符）
    can_ide_t IDE;  // 帧 ID 格式
    can_rtr_t RTR;  // 帧类型
    u8 DLC;         // 数据数量，0~8
    u8 Data[8];     // 数据缓存
}can_msg_t;

// CAN 过滤器参数
typedef struct _can_filter_param_t
{
    u8 filter_num; // 过滤器编号，0~13。共14个过滤器，每个过滤器可以关联1个CAN接口，可重复配置过滤器，之前相同过滤器的配置将被覆盖。
    u8 fifo_num;   // FIFO编号，0~1。通过过滤器的数据包存放的FIFO，每个FIFO最多可存放3个数据包。
    can_ide_t ide; // 允许通过的ID格式，11位（标准格式）还是29位（扩展格式）。
    u32 id;        // 期望允许通过的ID，注意，不是每一个位都进行严格检查，取决于 id_mask。
    u32 id_mask;   // ID检查掩码，值为1的位代表该位需要进行检查，值为0的位代表该位不进行检查（忽略）。
}can_filter_param_t;


/**
 * 收到数据事件通知回调函数类型
 * @param msg 收到的数据包指针
 * @param s CAN接口对象，哪个CAN。
 * @param user_param 用户参数，自定义的回调函数参数
 * @return true:数据已经被处理（不会再继续调用下一个回调），false:数据未处理，继续调用下一个回调
*/
typedef bool (*can_rx_cb_t)(can_msg_t *msg, can_t *s, void *user_param);




extern can_t _CAN_CAN1_NAME;
extern can_t _CAN_CAN2_NAME;


/**
 * 初始化一个CAN接口外设。注意，本函数未初始化时钟（RCC）、GPIO、NVIC。
 * 在驱动配置文件中配置RCC、GPIO，需要单独调用 Nvic_Enable 开启对应外设中断门控。
 * 初始化后必须给CAN接口设置一个过滤规则才能接收数据。
 * @param obj CAN接口
 * @param Baud 波特率，注意波特率基于默认（配置文件中）的时钟频率计算，如果运行时修改时钟频率将会导致波特率错误。
*/
void Can_Init(can_t *obj, can_baud_t Baud);

/**
 * 设置过滤规则。给一个CAN接口关联过滤器并配置过滤器规则。只有拥有符合规则的ID号的数据包（一帧数据）才会被接收。
 * @param obj CAN接口
 * @param filter_param 过滤器参数。
*/
void Can_SetFilter(can_t *obj, can_filter_param_t *filter_param);

/**
 * 添加收到数据事件回调函数。事件回调函数运行在 polsys 任务处理线程，通常是 main 线程。
 * @param obj CAN接口
 * @param cb 回调函数
 * @param user_param 回调函数的用户自定义参数，可以为NULL。
 * @return true:添加成功；false:添加失败，内存不足。
*/
bool Can_AddRxCb(can_t *obj, can_rx_cb_t cb, void *user_param);

/**
 * 【异步】发送一帧数据。缓存要发送的数据，提交发送申请。如果缓存满了会阻塞一小会，之后依然没有空间会返回false。
 * 注意，硬件有3个发送邮箱，相当于3帧缓存，目前未使用软件缓存，考虑后续增加软件缓存功能。
 * @param obj CAN接口
 * @param msg 要发送的数据包的指针，函数返回后可以释放。
 * @return true:数据已缓存到发送队列；false:缓存失败，长时间满状态，可能是CAN总线错误导致数据发不出去。
*/
bool Can_Send(can_t *obj, can_msg_t *msg);

#ifdef __cplusplus
}
#endif

#endif
